﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace CacheEngine
{
    /// <summary>缓存引擎扩展</summary>
    public static class CacheEngineExtenstions
    {
        /// <summary></summary>
        /// <typeparam name="TObj"></typeparam>
        /// <typeparam name="TRet"></typeparam>
        /// <param name="obj"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static TRet HitCache<TObj, TRet>(this TObj obj, Expression<Func<TObj, TRet>> expression)
        {
            var methodCallExpression = (MethodCallExpression)expression.Body;
            var methodInfo = methodCallExpression.Method;
            object[] args = GetArgumentsFromExpresion(methodCallExpression);
            bool doWeCache = false;
            ICacheManager manager;
            doWeCache = CacheManagerFactory.Current.TryGetCacheManager(methodInfo, out manager);
            if (doWeCache)
            {
                TRet cachedItem = default(TRet);
                bool cacheHit = manager.TryGetCacheItem(methodInfo, args, out cachedItem);
                if (cacheHit)
                    return cachedItem;
            }
            TRet retVal = expression.Compile().Invoke(obj);
            if (doWeCache)
                manager.SaveOrUpdate(typeof(TObj), methodInfo, args, retVal);

            return retVal;
        }

        private static object[] GetArgumentsFromExpresion(MethodCallExpression expression)
        {
            List<object> args = new List<object>();
            foreach (var argument in expression.Arguments)
            {
                if (argument.NodeType == ExpressionType.Constant)
                {
                    args.Add(((ConstantExpression)argument).Value);
                }
                else if (argument.NodeType == ExpressionType.MemberAccess)
                {
                    var ex = (MemberExpression)argument;
                    object val = ((ConstantExpression)ex.Expression).Value;
                    args.Add(((FieldInfo)ex.Member).GetValue(val));
                }
            }

            if (expression.Arguments.Count != args.Count)
                throw new Exception("Error getting one of the arguments values");
            return args.ToArray();
        }
    }
}
